/*
	Copyright (C) 2008  Alex Kemp <kemp00@gmail.com>

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include <stdio.h>
//#include "../commdefs.h"
#include "tga_util.h"
#include "csinclude.h"


CS_IMPLEMENT_FOREIGN_DLL
/*CS_IMPLEMENT_APPLICATION*/


//#define WANTDEBUGMSGS 1

#define _CS_ALLOC_ALIGN 4

#ifndef Malloc
#define Malloc(x) CS::Memory::AlignedMalloc (x, _CS_ALLOC_ALIGN)
#else
#error can't define Malloc, already defined
#endif

#ifndef Free
#define Free(x) CS::Memory::AlignedFree (x)
#else
#error can't define Free, already defined
#endif

#ifndef Realloc
#define Realloc(p, s) CS::Memory::AlignedRealloc (p, s, _CS_ALLOC_ALIGN)
#else
#error can't define Realloc, already defined
#endif


int lastfileout = 0;
csTicks elapsed_time = 0;
FILE * outfile;


//static csRef<iObjectRegistry> object_reg;
static iObjectRegistry * object_reg;

bool HandleEvent (iEvent &ev);






class Overlord
{
	private:
		csRef<iVFS> vfs;
		csRef<iEngine> engine;
		csRef<iLoader> loader;
		csRef<iGraphics3D> g3d;
		csRef<iKeyboardDriver> kbd;
//		csRef<iKeycomposer> keycomp;
		csRef<iVirtualClock> vc;	
	
		csRef<iPluginManager> plugin_mgr;
		csRef<iEventQueue> event_que;
		csRef<iEventNameRegistry> evtname_reg;
		csRef<iCommandLineParser> cmdline_prs;
		csRef<iVerbosityManager> verbos_mgr;
		csRef<iConfigManager> config_mgr;
		csRef<iGraphicsMemory> gfxmem;
		csRef<iStringSet> strset;

//		csRef<iMeshObjectType> mot;

		iGraphics2D* g2d;
		csRef<iView> view;
		iSector* room;
		float rotX, rotY;
		float sw_accel, sw_speed, sw_position;
		int set_up;

		
		int fly;

		float move_speed;

	public:
		Overlord () { fly = 1; set_up = 0; move_speed = 5.0; };
		~Overlord ()
		{
			/*if (object_reg) csInitializer::CloseApplication (object_reg);*/
			
		};

		// FA = Fail As 
		#define FA(x) { printf ("%s() failed\n", x); return 0; }
		
		int init (int argc, char* argv[], int screen_x, int screen_y)
		{
			/* 0 == failure */
		
			// don't change the order below
   			CS_INITIALIZE_PLATFORM_APPLICATION
    		if (!csInitializer::InitializeSCF (argc, argv)) FA("InitializeSCF")
    		if (!(object_reg = csInitializer::CreateObjectRegistry ())) FA("CreateObjectRegistry")
			if (!(cmdline_prs = csInitializer::CreateCommandLineParser (object_reg, argc, argv))) FA("CreateCommandLineParser")

			/*
			FILE * f = fopen ("apppath.txt", "wb");
			if (f)
			{
				fprintf(f, "->GetAppDir () = %s\n", cmdline_prs->GetAppDir ());
				fprintf(f, "->GetResourceDir () = %s\n", cmdline_prs->GetResourceDir ());
				fclose (f);
			}
			*/

    		if (!(plugin_mgr = csInitializer::CreatePluginManager (object_reg))) FA("CreatePluginManager")
    		if (!(event_que = csInitializer::CreateEventQueue (object_reg))) FA("CreateEventQueue")
			if (!(vc = csInitializer::CreateVirtualClock (object_reg))) FA("CreateVirtualClock")
			if (!(verbos_mgr = csInitializer::CreateVerbosityManager (object_reg))) FA("CreateVerbosityManager")
			if (!(config_mgr = csInitializer::CreateConfigManager (object_reg))) FA("CreateConfigManager")
			if (!csInitializer::CreateInputDrivers (object_reg)) FA("CreateInputDrivers")
			if (!csInitializer::CreateStringSet (object_reg)) FA("CreateStringSet")
			if (!csPlatformStartup (object_reg)) FA("csPlatformStartup")
			
			if (!(vfs = csInitializer::SetupVFS (object_reg, "crystalspace.kernel.vfs"))) FA("SetupVFS")
			/*
			if (f = fopen ("apppath.txt", "ab"))
			{
				fprintf (f, "vfs->GetCwd () = %s\n", vfs->GetCwd ());
				fclose (f);
			}
			*/
			
			char buf [1024];
			const char * rsrcdir = cmdline_prs->GetResourceDir ();
			int succ;

			sprintf (buf, "%s\\data\\", rsrcdir);
			succ = vfs->Mount ("/", buf);
			////////if (succ && (f = fopen ("apppath.txt", "ab"))) { fprintf (f, "mounted %s\n", buf); fclose (f); }
			sprintf (buf, "%s\\data\\", rsrcdir);
			succ = vfs->Mount ("/mydata", buf);
			//////if (succ && (f = fopen ("apppath.txt", "ab"))) { fprintf (f, "mounted %s\n", buf); fclose (f); }
		
			/*
			
			//showdir ("/");			
			showdir ("/mydata/");			
			showdir ("/mydata/gfx/");			
			showdir ("/mydata/models/");			
			*/

			/*			
			sprintf (buf, "%s\\data\\", rsrcdir);
			succ = vfs->Mount ("/data", buf);
			sprintf (buf, "%s\\data\\config\\", rsrcdir);
			succ = vfs->Mount ("/config", buf);
			sprintf (buf, "%s\\data\\config\\", rsrcdir);
			succ = vfs->Mount ("/config", buf);
			*/

			
				
			/* TODO: list here all plugins used */
			
			if (!csInitializer::RequestPlugins (object_reg,
				CS_REQUEST_PLUGIN("crystalspace.graphics2d.memory",iGraphicsMemory),
				CS_REQUEST_PLUGIN("crystalspace.graphics3d.software",iGraphics3D),
			//	CS_REQUEST_PLUGIN("crystalspace.graphics3d.opengl",iGraphics3D),
				CS_REQUEST_PLUGIN("crystalspace.engine.3d", iEngine),
				CS_REQUEST_PLUGIN("crystalspace.graphic.image.io.multiplexer", iImageIO),
				CS_REQUEST_PLUGIN("crystalspace.level.loader", iLoader),
				CS_REQUEST_PLUGIN("crystalspace.utilities.reporter", iReporter),
				CS_REQUEST_PLUGIN("crystalspace.utilities.stdrep", iStandardReporterListener),
			//	CS_REQUEST_PLUGIN("crystalspace.mesh.object.haze", iMeshObjectType),
				CS_REQUEST_END))
			return printf ("Failed to initialize plugins!\n");

			csEventID events [] = { csevFrame(object_reg), csevQuit(object_reg), csevKeyboardEvent(object_reg), CS_EVENTLIST_END };
			if (!csInitializer::SetupEventHandler (object_reg, &HandleEvent, events)) FA ("SetupEventHandler")

			if (!(g3d = csQueryRegistry<iGraphics3D> (object_reg))) FA ("csQueryRegistry<iGraphics3d>")
			if (!(engine = csQueryRegistry<iEngine> (object_reg))) FA ("csQueryRegistry<iEngine>")
			if (!(loader = csQueryRegistry<iLoader> (object_reg))) FA ("csQueryRegistry<iLoader>")
			g2d = g3d->GetDriver2D ();
			if (!(kbd = csQueryRegistry<iKeyboardDriver> (object_reg))) FA ("csQueryRegistry<iKeyboardDriver>")

//			if (!(mot = csQueryRegistry<iMeshObjectType> (object_reg))) FA ("csQueryRegistry<iMeshObjectType>")
			if (!(strset = csQueryRegistryTagInterface<iStringSet> (object_reg, "crystalspace.shared.stringset"))) FA ("csQueryRegistryTagInterface<iStringSet> (object_reg, \"crystalspace.shared.stringset\")")

			if (!setup_keyboard ()) FA("setup_keyboard")

			if (!csInitializer::OpenApplication (object_reg)) FA ("OpenApplication")

			if (!setup_scene ()) FA("setup_scene")

			return 1;
		};

		void showdir (char * dir)
		{
			csRef <iStringArray> files = vfs->FindFiles (dir);
			const char * file;
			int i_fmax = files->GetSize (), i_f;
			//while (file = files->Pop ())
			for (i_f = 0; i_f < i_fmax; i_f ++)
			{
				file = files->Get (i_f);
				printf ("%s\n", file);
				fflush (stdout);
			}
			//files->DecRef ();
		};

		
		int prepare_commandline (int screen_x, int screen_y)
		{
			// force memory canvas
			int i; const char * n;
			for (i = 0; (n = cmdline_prs->GetOptionName (i)) && strcmp("canvas", n); i++);
			if (n)
				cmdline_prs->ReplaceOption ("canvas", "memory", i);
			else
				cmdline_prs->AddOption ("canvas", "memory");
			
			// force resolution
			// TODO: make this work (now it's hardcoded to read config/video.cfg)
			/*
			config_mgr->SetInt ("Video.ScreenWidth", screen_x);
			config_mgr->SetInt ("Video.ScreenHeight", screen_y);
			config_mgr->SetInt ("Video.ScreenDepth", 16);
			*/
			
			/*
			char modestr [64];
			//sprintf (modestr, "%dx%d", screen_x, screen_y);
			sprintf (modestr, "800x600");
			for (i = 0; (n = cmdline_prs->GetOptionName (i)) && strcmp("mode", n); i++);
			if (n)
				cmdline_prs->ReplaceOption ("mode", modestr, i);
			else
				cmdline_prs->AddOption ("mode", modestr);
			*/
			
			return 1;
		}


		/*
			this class can be used to store a value
			associated with a specific key, such
			as actions to-be-executed
		*/
		class actionkeys
		{
			private:
				typedef struct
				{
					int key;
					int state;
				} keystruct;
				keystruct * keys;
				int nkeys;
			public:
				actionkeys ()
				{
					keys = 0;
					nkeys = 0;
				};
				~actionkeys ()
				{
					if (keys)
					{
						//free (keys);
						Free (keys);
					}
					keys = 0;
				};
				/* sets 'success' to non-zero on success */
				/* then the result is a valid index */
				int findkey (int key, int * success)
				{
					assert (keys != 0);
					int i;
					*success = 1;
					for (i = 0; i < nkeys; i++)
					{
						if (key != keys[i].key)
						{
							continue;
						}
						return i;
					}
					*success = 0;
					return -1;
				}
				/* returns non-zero on success */
				int addkey (int key, int state)
				{
					int success;
					/* allocate new */
					if (!keys)
					{
						//keys = (keystruct *)malloc (sizeof (keystruct));
						keys = (keystruct *)Malloc (sizeof (keystruct));
						if (!keys)
						{
							return 0;
						}
					}
					/* extend */
					else
					{
						// filter duplicates
						findkey (key, &success);
						if (success)
						{
							return 0;
						}
						// increase alloc space
						//keystruct * newp = (keystruct *)realloc (&keys, (nkeys+1) * sizeof (keystruct));
						keystruct * newp = (keystruct *)Realloc (keys, (nkeys+1) * sizeof (keystruct));
						if (!newp)
						{
							return 0;
						}
						keys = newp;
					}
					/* add new */
					keys[nkeys].key = key;
					keys[nkeys].state = state;
					/* update */
					nkeys ++;
					
					return 1;
				};
				/* returns -1 on fail */
				int setstate (int key, int state)
				{
					assert (keys != 0);
					int success;
					int k = findkey (key, &success);
					if (!success)
					{
						return -1;
					}
					keys[k].state = state;
					return 0;
				}
				/* returns 0 on fail */
				int getstate (int key)
				{
					assert (keys != 0);
					int success;
					int k = findkey (key, &success);
					if (!success)
					{
						return 0;
					}
					return keys[k].state;
				}
				/* always works */
				void reset ()
				{
					int i;

					if (!keys)
					{
						return;
					}
					
					for (i = 0; i < nkeys; i++)
					{
						if (keys[i].state)
						{
							keys[i].state = 0;
						}
					}
				}
		} ak;


		class cubefactory
		{

			private:
			
				csRef <iEngine> engine;
				csRef<iSprite3DFactoryState> mything_fstate;
				iMaterialWrapper * insmat;
				iMaterialWrapper * outsmat;
			
			public:
			
				int init (iEngine * eng, iMaterialWrapper * im, iMaterialWrapper * om)
				{
					insmat = im;
					outsmat = om;
					engine = eng;
					return 1;
				};
			
				csRef <iMeshWrapper> create_cube (iSector * sect, float x, float y, float z, float size)
				{

					int i;

					size = size / 2;
					csRef<iMeshFactoryWrapper> mt_mfw = engine->CreateMeshFactory ("crystalspace.mesh.object.sprite.3d", "cube4dfactory");
					
					if (!mt_mfw) {fprintf (stderr, "** !mt_mfw\n"); fflush (stderr); return 0; }
					
					//csRef<iMeshWrapper> mything = engine->CreateThingMesh (sect, "cube");


				/*	
					//csRef<iThingState> mts = scfQueryInterface<iThingState> (mything->GetMeshObject ());
					csRef<iSprite3DState> mts = scfQueryInterface<iSprite3DState> (mything->GetMeshObject ());

					if (!mts) {fprintf (stderr, "** !mts\n"); fflush (stderr); return 0; }

					iMeshObject* mything_object = mything->GetMeshObject ();
					
					if (!mything_object) {fprintf (stderr, "** !mything_object\n"); fflush (stderr); return 0; }

				*/

					
					iMeshObjectFactory* mything_factory = mt_mfw->GetMeshObjectFactory ();

					if (!mything_factory) {fprintf (stderr, "** !mything_factory\n"); fflush (stderr); return 0; }
					
					mything_fstate = scfQueryInterface<iSprite3DFactoryState> (mything_factory);
					
					if (!mything_fstate) {fprintf (stderr, "** !mything_fstate\n"); fflush (stderr); return 0; }

					
					
					//
					// sprite3D
					//
					//
					
					
					// add an action
					csRef<iSpriteAction> baseaction = mything_fstate->AddAction ();
					if (!baseaction) { fprintf (stderr, "iSprite3DFactoryState->AddAction() failed!\n"); fflush (stderr); return 0; }
					//baseaction->IncRef ();
					baseaction->SetName ("baseaction");

					#ifdef WANTDEBUGMSGS
					fprintf (stderr, "** base action set (action count = %d)\n", mything_fstate->GetActionCount ());
					fflush (stderr);
					#endif


					
					
					// add a frame
					csRef<iSpriteFrame> baseframe = mything_fstate->AddFrame ();
					if (!baseframe) { fprintf (stderr, "iSprite3DFactoryState->AddFrame() failed!\n"); fflush (stderr); return 0; }
					//baseframe->IncRef ();
					baseframe->SetName ("baseframe");
					baseaction->AddFrame (baseframe, 1000, 0);

					#ifdef WANTDEBUGMSGS
					fprintf (stderr, "** frame name set\n");
					fflush (stderr);
					#endif

					
					// set number of vertices in mesh
					mything_fstate->AddVertices (48);

					#ifdef WANTDEBUGMSGS
					fprintf (stderr, "** vertexes added\n");
					fflush (stderr);
					#endif

					
					// set individual vertices for frame 0
					// we are going to manipulate these always
					// in frame 0, there are no other frames

					i = 0;

					#define SET_VERTICES\
					/* top */																\
					mything_fstate->SetVertex (0, i++, csVector3 (0-size, 0+size, 0+size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0+size, 0+size, 0+size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0+size, 0+size, 0-size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0-size, 0+size, 0-size));	\
																							\
					/* right */																\
					mything_fstate->SetVertex (0, i++, csVector3 (0+size, 0+size, 0-size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0+size, 0+size, 0+size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0+size, 0-size, 0+size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0+size, 0-size, 0-size));	\
																							\
					/* back */																\
					mything_fstate->SetVertex (0, i++, csVector3 (0+size, 0+size, 0+size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0-size, 0+size, 0+size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0-size, 0-size, 0+size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0+size, 0-size, 0+size));	\
																							\
					/* left */																\
					mything_fstate->SetVertex (0, i++, csVector3 (0-size, 0+size, 0+size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0-size, 0+size, 0-size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0-size, 0-size, 0-size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0-size, 0-size, 0+size));	\
																							\
					/* front */																\
					mything_fstate->SetVertex (0, i++, csVector3 (0-size, 0+size, 0-size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0+size, 0+size, 0-size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0+size, 0-size, 0-size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0-size, 0-size, 0-size));	\
																							\
					/* bottom */															\
					mything_fstate->SetVertex (0, i++, csVector3 (0-size, 0-size, 0-size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0+size, 0-size, 0-size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0+size, 0-size, 0+size));	\
					mything_fstate->SetVertex (0, i++, csVector3 (0-size, 0-size, 0+size));

					// for the outside
					SET_VERTICES
					// for the inside
					SET_VERTICES
					
					// they need to be separate, because they'll
					// have different texture UV coords

					#undef SET_VERTICES






					#ifdef WANTDEBUGMSGS
					fprintf (stderr, "** vertexes set\n");
					fflush (stderr);
					#endif
					



					//
					// sprite3d
					//
					//
					#define ADD_VERTS_POLY(v0, v1, v2)\
					mything_fstate->AddTriangle (v0, v1, v2);


					// add faces

					//
					// outside

					for (i = 0; i < 24; i += 4)
					{
						ADD_VERTS_POLY (i+0, i+1, i+3);
						ADD_VERTS_POLY (i+1, i+2, i+3);
					}

					#ifdef WANTDEBUGMSGS
					fprintf (stderr, "** outside faces added\n");
					fflush (stderr);
					#endif


					//
					// inside					

					for (i = 24; i < 48; i += 4)
					{
						ADD_VERTS_POLY (i+3, i+2, i+0);
						ADD_VERTS_POLY (i+2, i+1, i+0);
					}

					#ifdef WANTDEBUGMSGS
					fprintf (stderr, "** all triangles added\n");
					fflush (stderr);
					#endif


					#undef ADD_VERTS_POLY 

					//
					// sprite3d
					//
					mything_factory->SetMaterialWrapper (insmat);


					#ifdef WANTDEBUGMSGS
					fprintf (stderr, "** material set\n");
					fflush (stderr);
					#endif




					csFlags f = mt_mfw->GetFlags ();
					f.SetAll (0);
					f.Set (CS_ENTITY_NOSHADOWS | CS_ENTITY_NOLIGHTING | CS_ENTITY_NOCLIP);


					#ifdef WANTDEBUGMSGS
					fprintf (stderr, "** flags set\n");
					fflush (stderr);
					#endif


					mt_mfw->SetZBufMode (CS_ZBUF_NONE);
					mt_mfw->SetRenderPriority (engine->GetAlphaRenderPriority ());

					#ifdef WANTDEBUGMSGS
					fprintf (stderr, "** zbuf mode set\n");
					fprintf (stderr, "** render priority set\n");
					fflush (stderr);
					#endif



					// align the texture
					
					csVector2 texels [48];

					// inside
					//
					
					for (i = 0; i < 24; i += 4)
					{					
						texels[i+0].x = 0.5; texels[i+0].y = 0;
						texels[i+1].x = 1; texels[i+1].y = 0;
						texels[i+2].x = 1; texels[i+2].y = 1;
						texels[i+3].x = 0.5; texels[i+3].y = 1;
					}

					// outside
					//
					
					for (i = 24; i < 48; i += 4)
					{					
						texels[i+0].x = 0.5; texels[i+0].y = 0;
						texels[i+1].x = 0; texels[i+1].y = 0;
						texels[i+2].x = 0; texels[i+2].y = 1;
						texels[i+3].x = 0.5; texels[i+3].y = 1;
					}

					mything_fstate->SetTexels (texels, 0);



					// sprite3d
					csRef<iMeshWrapper> mything = mt_mfw->CreateMeshWrapper ();
					if (!mything) {fprintf (stderr, "** !mything\n"); fflush (stderr); return 0; }
					
					#ifdef WANTDEBUGMSGS
					fprintf (stderr, "** mything created\n");
					fflush (stderr);
					#endif

					// incref, because the engine will own a copy
					mything->IncRef ();

					csRef <iMovable> mov = mything->GetMovable ();
					if (!mov) {fprintf (stderr, "** iMeshWrapper->GetMovable() failed\n"); fflush (stderr); return 0; }
					mov->SetSector (sect);
					mov->SetPosition (csVector3 (x, y, z));
					mov->UpdateMove ();

					//return mything;
					return mything;
				};
				
				void get_texel (int tex, float & x, float & y)
				{
					csVector2 v2 = mything_fstate->GetTexel (0, tex);
					x = v2.x;
					y = v2.y;
				};

				void set_texel (int tex, float x, float y)
				{
					mything_fstate->SetTexel (0, tex, csVector2 (x, y));
				};
				
				int get_ntexels ()
				{
					return mything_fstate->GetVertexCount ();
				};
		} cf;


		csRef <iMeshWrapper> cube4d;
		
		void movecube (iMeshWrapper * c, float x, float y, float z, float t)
		{
			/*
			csVector3 pos = c->GetMovable ()->GetPosition ();
			pos.x += x;
			pos.y += y;
			pos.z += z;
			c->GetMovable ()->SetPosition (pos);
			c->GetMovable ()->UpdateMove ();
			*/			
			
			csRef<iSprite3DFactoryState> fs = scfQueryInterface<iSprite3DFactoryState> (c->GetFactory ()->GetMeshObjectFactory ());
			//if (!fs) { fprintf (stderr, "***!fs\n"); fflush (stderr); return; }
			csVector3 * verts = fs->GetVertices (0);
			
			int i;
			int imax = fs->GetVertexCount ();
			for (i = 0; i < imax; i++)
			{
				verts[i].x += x;
				verts[i].y += y;
				verts[i].z += z;
				
				// add 4D here
			}
		};
		
		
		int setup_scene ()
		{
			view.AttachNew(new csView (engine, g3d));
			view->SetRectangle (0, 0, g2d->GetWidth (), g2d->GetHeight ());
			engine->SetLightingCacheMode (0);

			if (!(loader->LoadTexture ("grass", "/mydata/gfx/test2.bmp"))) FA ("LoadTexture (test2.png)")
			iMaterialWrapper* tm = engine->GetMaterialList ()->FindByName ("grass");
			room = engine->CreateSector ("room");

//			if (!(loader->LoadTexture ("sky", "/mydata/gfx/sky.png"))) FA ("LoadTexture (gfx/sky.png)")

			
			csRef<iMeshWrapper> walls (engine->CreateSectorWallsMesh (room, "walls"));
			iMeshObject* walls_object = walls->GetMeshObject ();
			iMeshObjectFactory* walls_factory = walls_object->GetFactory();
			csRef<iThingFactoryState> walls_state = scfQueryInterface<iThingFactoryState> (walls_factory);
			walls_state->AddInsideBox (csVector3 (-32, 0, -5), csVector3 (32, 20, 25));
			walls_state->SetPolygonMaterial (CS_POLYRANGE_LAST, tm);
			walls_state->SetPolygonTextureMapping (CS_POLYRANGE_LAST, 3);
			//walls->setZBufMode (CS_ZBUF_FILL);

			//if (!(loader->LoadTexture ("outside", "/mydata/gfx/out.png"))) FA ("LoadTexture (outside.png)")
			if (!(loader->LoadTexture ("outside", "/mydata/gfx/out.jpg"))) FA ("LoadTexture (outside.png)")
			//if (!(loader->LoadTexture ("inside", "/mydata/gfx/in.png"))) FA ("LoadTexture (inside.png)")
			if (!(loader->LoadTexture ("inside", "/mydata/gfx/cube4d_t.png"))) FA ("LoadTexture (cube4d_t.png)")
			iMaterialWrapper* insmat = engine->GetMaterialList ()->FindByName ("inside");
			iMaterialWrapper* outsmat = engine->GetMaterialList ()->FindByName ("outside");
			if (!insmat) FA ("!insmat");
			if (!outsmat) FA ("!outsmat");




			cf.init (engine, insmat, outsmat);
			cube4d = cf.create_cube (room, -1, 2, 2.5, 2);
			fprintf (stderr, "******** cube4d created ******\n");
			fflush (stderr);

	




			csRef<iLight> light;
			iLightList* ll = room->GetLights ();
			light = engine->CreateLight(0, csVector3(-6, 3, 4), 10, csColor(1, 1, 1));
			ll->Add (light);
			light = engine->CreateLight(0, csVector3(3, 5,  0), 10, csColor(1, 1, 1));
			ll->Add (light);
			
			light = engine->CreateLight(0, csVector3(14, 5, 15), 10, csColor(1, 1, 1), CS_LIGHT_DYNAMICTYPE_STATIC);
			ll->Add (light);
			
			csRef<iLight> bw_light = light;
			
		
			
			
			light = engine->CreateLight(0, csVector3(20, 10, 5), 75, csColor(0, 1, 0));
			ll->Add (light);




			
			room->SetDynamicAmbientLight (csColor(1.0, 1.0, 1.0));
			
			engine->Prepare ();


			rotY = rotX = 0;
  			view->GetCamera ()->SetSector (room);
			view->GetCamera ()->GetTransform ().SetOrigin (csVector3 (0, 5, -3));
		
			set_up = 1;
		
			return 1;
		}

		#undef FA
		
		int run ()
		{
			if (fly)
			{
				vc->Advance ();
				event_que->Process ();
				return 1;
			}
			return 0;
		};
		
		void dont_run (int skipquit) // pass 0 here unless reacting to a csevQuit
		{
			if (skipquit == 0)
			{
				event_que->GetEventOutlet()->Broadcast(csevQuit(object_reg));
				run ();
			}
			fly = 0;
		};

		int setup_keyboard ()
		{
			int success;
			#ifndef ADDKEY
			#define ADDKEY(x)\
			success = ak.addkey (x, 0);\
			if (!success) return 0;
			#else
			#error can't define ADDKEY, already defined
			#endif
			ADDKEY ('a'); // move left		( x- )
			ADDKEY ('d'); // move right		( x+ )
			ADDKEY ('w'); // move forward	( z+ )
			ADDKEY ('s'); // move backward	( z- )
			ADDKEY ('r'); // move up		( y+ )
			ADDKEY ('f'); // move down		( y- )
			ADDKEY ('q'); // move future	( t+ )
			ADDKEY ('e'); // move past		( t- )
			ADDKEY (CSKEY_SHIFT); // enable rotation
			#undef ADDKEY
			ak.reset ();
			return 1;
		};
		
		int isrunning ()
		{
			return fly;
		}

		bool handle_keyboard (iEvent &ev)
		{
			
			#ifdef WANTDEBUGMSGS
			fprintf (stderr, "got keyboard event\n");
			fflush (stderr);
			#endif
			csKeyEventType eventtype = csKeyEventHelper::GetEventType(&ev);

			if (eventtype == csKeyEventTypeDown)
			{
				#ifdef WANTDEBUGMSGS
				printf ("eventtype is csKeyEventTypeDown\n");
				#endif

				utf32_char ccode;
				utf32_char rcode;
				ccode = csKeyEventHelper::GetCookedCode(&ev);
				rcode = csKeyEventHelper::GetRawCode(&ev);

				/* handle here keys that need to be pressed only once,
				   but shouldn't be held */

				if (rcode == CSKEY_ESC)
				{
					printf ("got CSKEY_ESC\n");
					event_que->GetEventOutlet()->Broadcast(csevQuit(object_reg));
					return true;
				}
				else if (rcode == CSKEY_ENTER)
				{
					#ifdef WANTDEBUGMSGS
					printf ("got CSKEY_ENTER\n");
					#endif
					screenshot ();
					return true;
				}
				else if (rcode == CSKEY_PADPLUS && move_speed < 10.0)
				{
					move_speed += 0.1;
					fprintf (stderr, "move_speed = %.01f\n", move_speed);
					fflush (stderr);
				}
				else if (rcode == CSKEY_PADMINUS && move_speed > 0.1)
				{
					move_speed -= 0.1;
					fprintf (stderr, "move_speed = %.01f\n", move_speed);
					fflush (stderr);
				}

				
					/* handle here keys that can be held pressed */
				else
				{
					if (rcode)
					{
						#ifdef WANTDEBUGMSGS
						fprintf (stderr, "raw key code = %d, cooked code = %d\n", rcode, ccode);
						fflush (stderr);
						#endif
					}
					//if (rcode == 'a' || rcode == 'd' || rcode == 'w' || rcode == 's' || rcode == 'r' || rcode == 'f' || rcode == 'q' || rcode == 'e'
					//)
					{ 
						return ak.setstate (rcode, 1) ? true : false;
					}
				}
			}
			else if (eventtype == csKeyEventTypeUp)
			{
				utf32_char ccode;
				utf32_char rcode;
				ccode = csKeyEventHelper::GetCookedCode(&ev);
				rcode = csKeyEventHelper::GetRawCode(&ev);

				return ak.setstate (rcode, 0) ? true : false;
			}
			fflush (stdout);
			return false;
		};

		bool handle_frame (iEvent &ev)
		{
			float x, y;
			
			/* move */
			if (set_up && view)
			{
				#define CHKKEY(x) kbd->GetKeyState (x)

				float speed = (vc->GetElapsedTicks () / 1000.0) * (0.06 * 20);
				iCamera* c = view->GetCamera();
				if (CHKKEY (CSKEY_RIGHT)) rotY += speed;
				if (CHKKEY (CSKEY_LEFT)) rotY -= speed;
				if (CHKKEY (CSKEY_PGUP)) rotX += speed;
				if (CHKKEY (CSKEY_PGDN)) rotX -= speed;
				if (CHKKEY (CSKEY_UP)) c->Move (CS_VEC_FORWARD * 4 * speed); //else printf ("up is not\n");
				if (CHKKEY (CSKEY_DOWN)) c->Move (CS_VEC_BACKWARD * 4 * speed);
				float s = speed * move_speed;
				if (ak.getstate ('a')) movecube (cube4d, -s, 0, 0, 0);
				if (ak.getstate ('d')) movecube (cube4d, +s, 0, 0, 0);
				if (ak.getstate ('w')) movecube (cube4d, 0, 0, +s, 0);
				if (ak.getstate ('s')) movecube (cube4d, 0, 0, -s, 0);
				if (ak.getstate ('r')) movecube (cube4d, 0, +s, 0, 0);
				if (ak.getstate ('f')) movecube (cube4d, 0, -s, 0, 0);
				if (ak.getstate ('q')) movecube (cube4d, 0, 0, 0, +s);
				if (ak.getstate ('e')) movecube (cube4d, 0, 0, 0, -s);
				//ak.reset ();
				
				csMatrix3 rot = csXRotMatrix3 (rotX) * csYRotMatrix3 (rotY);
				csOrthoTransform ot (rot, c->GetTransform().GetOrigin ());
				c->SetTransform (ot);
				#undef CHKKEY
				//printf ("f-\n");
			}
			/*
			else
			{
				printf ("not set_up && view\n");
			}
			*/
			#undef _DOMOVE


			//g3d->BeginDraw (engine->GetBeginDrawFlags () | CSDRAW_3DGRAPHICS | CSDRAW_CLEARSCREEN | CSDRAW_CLEARZBUFFER);
			g3d->BeginDraw (engine->GetBeginDrawFlags () | CSDRAW_3DGRAPHICS);
			view->Draw ();
			g3d->FinishDraw();
			g3d->Print(0);


			#if 0
			#ifdef WANT_SCREENSHOTS
			elapsed_time += vc->GetElapsedTicks ();
			if (elapsed_time >= 5000.0)
			{
				elapsed_time = 0;
				screenshot ();
				
				//if (lastfileout >= 2)
				//	dont_run (0);
			}
			#endif
			#endif

			return true;
		};
		
		unsigned char * get_videomem ()
		{
			return gfxmem ? gfxmem->GetImage () : 0;
		};
		
		void screenshot ()
		{
			int w = 800;//g2d->GetWidth ();
			int h = 600;//g2d->GetHeight ();
			unsigned char * mem = get_videomem ();
			char fn [128];
			sprintf (fn, "gfxmem%02d.tga", lastfileout++);
			
			if (mem)
				write_tga_image (fn, w, h, 16, mem, 0);
			else
				printf ("can't get videomem\n");
			 
		};
		
		const char * getcsstr (csStringID strid)
		{
			if (!strset)
			{
				return 0;
			}
			
			return strset->Request (strid);
		};
};



Overlord * o;



#ifdef WANTDEBUGMSGS
int finalmsgs = 0;
#endif
bool HandleEvent (iEvent &ev)
{
	if (!o)
	{
		return false;
	}
#ifdef WANTDEBUGMSGS
	if (finalmsgs)
	{
		fprintf (stderr, "HandleEvent: finalmsg ('%s')\n", o->getcsstr (ev.GetName ()));
		fflush (stderr);
	}
#endif
	if (ev.Name == csevFrame(object_reg))
	{
		return o->handle_frame (ev);
	}
	else if (CS_IS_KEYBOARD_EVENT(object_reg, ev))
	{
		return o->handle_keyboard (ev);
	}
	else if (ev.Name == csevQuit (object_reg))
	{
#ifdef WANTDEBUGMSGS
		fprintf (stderr, "HandleEvent: about to call o->dontrun ()\n");
		fflush (stderr);
#endif
		o->dont_run (1);
#ifdef WANTDEBUGMSGS
		fprintf (stderr, "HandleEvent: o->dontrun () returned \n");
		fflush (stderr);
		finalmsgs = 1;
#endif
		return true;
	}

	return false;
}



/* **************** interface ************ */
 
extern "C" int overlord_init (int argc, char* argv[], int screen_x, int screen_y)
{
	/*
	outfile = fopen ("out.txt", "w");
	if (!outfile)
	{
		printf ("can't open console output file\n");
		return 0;
	}
	*/

	o = new Overlord ();
	if (!o)
	{
		printf ("failed to create Overlord class\n");
		return 0;
	}
	if (!o->init (argc, argv, screen_x, screen_y))
	{
		printf ("failed to initialize Overlord class\n");
		return 0;
	}
	return 1;
}


extern "C" int overlord_run ()
{
	return o->run ();
}


extern "C" unsigned char * overlord_get_videomem ()
{
	return o->get_videomem ();
}


extern "C" int overlord_destroy ()
{
	if (o)
	{
		if (o->isrunning ())
		{
			o->dont_run (0);
		}
		delete o;
		o = 0;
		printf ("overlord_destroy(): about to call DestroyApplication\n");
		fflush (stdout);
		
		csInitializer::DestroyApplication (object_reg);
		object_reg = 0;
		printf ("DestroyApplication() returned\n");
		fflush (stdout);
		return 1;
	}
	
	/*
	if (outfile)
	{
		fclose (outfile);
	}
	*/
	
	return 0;
}




/*
 int main (int argc, char* argv[]) 
 {
   return overlord_init (argc, argv, 800, 600);
 }
*/
